package midoo.server.core;

import com.google.common.base.Strings;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import midoo.common.MConfig;
import midoo.common.MService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Map;

/**
 * Created by ajianzheng on 2015/4/10.
 *
 */
public class MServerBootstrap implements MService {
    protected static final Logger LOGGER = LoggerFactory.getLogger(MServerBootstrap.class);

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    public MServerBootstrap() {
    }

    @Override
    public int start(MConfig cfg) throws Exception {
        final MServerBootstrapConfig config = (MServerBootstrapConfig) cfg;

        bossGroup = new NioEventLoopGroup(config.getBossThreadCount(),
                new DefaultThreadFactory("netty-boss"));
        workerGroup = new NioEventLoopGroup(config.getWorkerThreadCount(),
                new DefaultThreadFactory("netty-worker"));

        ///
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class);

        for (Map.Entry<ChannelOption, Object> entry : config.getOptions().entrySet()) {
//            System.err.println(entry.getKey().name() + ":" + entry.getValue());
            serverBootstrap.option(entry.getKey(), entry.getValue());
        }

        for (Map.Entry<ChannelOption, Object> entry : config.getChildOptions().entrySet()) {
//            System.err.println(entry.getKey().name() + ":" + entry.getValue());
            serverBootstrap.childOption(entry.getKey(), entry.getValue());
        }

        if (config.isAddLastLoggingHandler()) {
//            serverBootstrap.handler(new LoggingHandler(config.getLogLevel()));
//            serverBootstrap.handler(new LoggingHandler(LogLevel.DEBUG));
        }

        serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();

                        if (config.isAddLastLoggingHandler()) {
//                            System.err.println("getLogLevel:" + config.getLogLevel());
//                            pipeline.addLast("logger", new LoggingHandler(config.getLogLevel()));
//                            pipeline.addLast("logger", new LoggingHandler(LogLevel.DEBUG));
                        }

                        pipeline.addLast("limit", new MChannelLimitHandler(config.getMaxConnections()));
                        pipeline.addLast("flow-stat", new MChannelFlowStatHandler());

                        pipeline.addLast("encoder", config.getEncoder().getClass().newInstance());
                        pipeline.addLast("decoder", config.getDecoder().getClass().newInstance());

                        if (config.isAddLastIdleStateHandler()) {
                            pipeline.addLast("idle", new IdleStateHandler(config.getReaderIdleTimeSeconds()
                                    , config.getWriterIdleTimeSeconds(), config.getAllIdleTimeSeconds()));
                        }

                        pipeline.addLast("handler", new MChannelReadHandler());
                    }
                });

        /// start
        serverBootstrap.bind(config.getHost(), config.getPort()).sync();

        LOGGER.info(layout(config.getServerName(), config.getHost(), config.getPort()));

        return 0;
    }

    @Override
    public void stop() {
        try {
            if (null !=bossGroup) bossGroup.shutdownGracefully().sync();
            if (null !=workerGroup) workerGroup.shutdownGracefully().sync();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    static String layout(String serverName, String host, int port) {
        int lineLength = 50;
        String CRLF = "\r\n";
        String START = "* ";
        String START2 = "*-";
        String END = "*";

        return String.format(CRLF
                        + Strings.repeat("*", lineLength+2) + END + CRLF
                        + START + Strings.padEnd(new Date().toString(), lineLength, ' ') + END + CRLF
                        + START2 + Strings.repeat("-", lineLength) + END + CRLF
                        + START + Strings.padEnd(serverName + " started ...", lineLength, ' ') + END + CRLF
                        + START + Strings.padEnd("Listening at " + host + ":" + port, lineLength, ' ') + END + CRLF
                        + START2 + Strings.repeat("-", lineLength) + END + CRLF
                        + START + Strings.padEnd("ServerBench: " + MServerBench.getInstance().getServerBenchVersion(), lineLength, ' ') + END + CRLF
                        + Strings.repeat("*", lineLength+2) + END + CRLF
        );
    }
}
